<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    body {
      margin: 0;
    }

    #toolBox {
      display: flex;
      padding: 0 0.5rem 0 0.5rem;
      height: 100vh;
      width: 2rem;
      justify-content: center;
      flex-direction: column;
      position: absolute;
      top: 0;
      left: 0;
    }

    canvas {
      position: fixed;
      left: 0;
      right: 0;
      bottom: 0;
      top: 0;
      z-index: -1;
    }

    .switch {
      border: none;
      outline: 0;
      padding: 0;
      position: relative;
      -webkit-user-select: none;
      -moz-user-select: none;
      -ms-user-select: none;
      user-select: none
    }

    .switch label {
      background: #ddd;
      color: transparent;
      cursor: pointer;
      display: inline-block;
      position: relative;
      text-indent: 100%;
      width: 2rem;
      height: 4rem;
      transition: left 0.15s ease-out
    }

    .switch input {
      left: 10px;
      opacity: 0;
      padding: 0;
      position: absolute;
      top: 9px
    }

    .switch input+label {
      margin-left: 0;
      margin-right: 0
    }

    .switch label:after {
      background: #fff;
      content: "a";
      display: inline-block;
      height: 1.5rem;
      left: .25rem;
      position: absolute;
      top: .25rem;
      width: 1.5rem;
      -webkit-transition: top 0.15s ease-out;
      -moz-transition: top 0.15s ease-out;
      -o-transition: translate3d(0, 0, 0);
      transition: top 0.15s ease-out;
      -webkit-transform: translate3d(0, 0, 0);
      -moz-transform: translate3d(0, 0, 0);
      -ms-transform: translate3d(0, 0, 0);
      -o-transform: translate3d(0, 0, 0);
      transform: translate3d(0, 0, 0)
    }

    .switch input:checked+label {
      background: #008CBA
    }

    .switch input:checked+label:after {
      top: 2.2rem
    }

    #coordinate {
      background-color: #fff;
      position: fixed;
      left: 0;
      bottom: 0;
      color: #008CBA;
      padding: .5rem;
    }

    #mask {
      position: fixed;
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      background-color: rgba(0, 0, 0, 0.3);
    }
  </style>
  <link rel="stylesheet" href="//at.alicdn.com/t/font_1809416_f1ykrfedthj.css">
  <title>paint</title>
  <script src="/socket.io/socket.io.min.js"></script>
  <script>
    let socket, currentArray, mask;
    connecting = index = 0;
    labelColors = ['#ddd', '#008CBA'];
    current = [[0, 0], [0, 0], 5, 'black'];
    strokeColors = ['white', 'black'];
    lineSizes = [25, 5];
    strokeColorIndex = 0;
    function init() {
      canvas = document.getElementById('paintCanvas');
      ctx = canvas.getContext('2d');
      connStatus = document.getElementById('connStatus');
      coordinate = document.getElementById('coordinate');
      proxy = new Proxy({}, {
        set(target, property, value) {
          if (property == 'offset') {
            if (JSON.stringify(target[property]) == JSON.stringify(value)) { return };
            target[property] = value;
            handleResize();
            coordinate.innerHTML = `${value[0]},${value[1]}`;
            target.cameraRange = [[value[0], value[0] + canvas.width], [value[1], value[1] + canvas.height]];
            if (proxy.socketStatus) {
              socket.emit('load', proxy.cameraRange, drawingArr);
              localStorage.setItem('publicOffset', JSON.stringify(value))
            } else {
              drawingArr();
              localStorage.setItem('privateOffset', JSON.stringify(value))
            }
          } else if (property == 'socketStatus') {
            if (target[property] == value) { return };
            handleResize();
            copy = target[property];
            localStorage.setItem('socketStatus', target[property] = value);
            if (value == true) {
              publicOffset = localStorage.getItem('publicOffset');
              socket = io();
              connecting = setInterval(() => (connStatus.style.background = labelColors[index++ % 2]), 1000);
              socket.on("connect", () => {
                socket.emit('load', proxy.cameraRange, drawingArr);
                clearInterval(connecting);
                connecting = index = 0;
                connStatus.style.background = 'none';
                arrayReg(publicOffset) ? proxy.offset = JSON.parse(publicOffset) : localStorage.setItem('publicOffset', JSON.stringify(proxy.offset = [0, 0]));
              });
              socket.on('connect_error', () => {
                if (connecting) { return }
                connecting = setInterval(() => (connStatus.style.background = labelColors[index++ % 2]), 1000);
              });
              socket.on('drawing', d => {
                if (d[0][0] < proxy.offset[0] && d[0][0] > proxy.offset[0] + canvas.width && d[0][1] < proxy.offset[1] && d[0][1] > proxy.offset[1] + canvas.height && d[1][0] < proxy.offset[0] && d[1][0] > proxy.offset[0] + canvas.width && d[1][1] < proxy.offset[1] && d[1][1] > proxy.offset[1] + canvas.height) { return }
                drawing(d, proxy.offset);
                currentArray.push(d)
              });
            } else if (value == false) {
              if (copy == true) {
                socket.close();
                clearInterval(connecting);
                connStatus.style.background = 'none';
                connecting = index = 0;
              }
              privateOffset = localStorage.getItem('privateOffset');
              /^\[\[\[.*\]\]$/.test(localStorage.getItem('private')) ? currentArray = JSON.parse(localStorage.getItem('private')) : localStorage.setItem('private', JSON.stringify(currentArray = []));
              arrayReg(privateOffset) ? proxy.offset = JSON.parse(privateOffset) : localStorage.setItem('privateOffset', JSON.stringify(proxy.offset = [0, 0]));
              drawingArr();
            }
          }
        }
      });

      document.getElementById('socketSwitch').checked = proxy.socketStatus = localStorage.getItem('socketStatus') == 'false' ? false : true;
      window.onresize = () => { handleResize(); proxy.socketStatus ? socket.emit('load', proxy.cameraRange, drawingArr) : drawingArr(currentArray) };
      canvas.addEventListener('mousedown', handleMouseDown);
      document.addEventListener('mouseup', handleMouseUp);
      canvas.addEventListener('touchstart', handleTouchStart);
      document.addEventListener('touchend', handleTouchUp);
      canvas.addEventListener('gesturestart', toggleColor);
    }

    function handleResize() {
      canvas.width = window.outerWidth;
      canvas.height = window.outerHeight;
    }
    function toggleColor() {
      current[2] = lineSizes[strokeColorIndex % 2];
      current[3] = strokeColors[strokeColorIndex % 2];
      strokeColorIndex++;
    }
    function arrayReg(str) {
      return /^\[-?(0|[1-9]\d*)(\.\d+)?,-?(0|[1-9]\d*)(\.\d+)?\]$/.test(str)
    }

    function handleMouseDown(e) {
      if (e.button == 0) {
        current[0] = current[1] = [e.x + proxy.offset[0], e.y + proxy.offset[1]];
        if (proxy.socketStatus) {
          socket.emit('drawing', current)
        } else {
          drawing(current, proxy.offset);
          currentArray.push(JSON.parse(JSON.stringify(current)))
        }
        document.addEventListener('mousemove', handleMousemove);
      } else if (e.button == 1) { toggleColor() }
    }
    function handleTouchStart(e) {
      e.preventDefault();
      current[0] = current[1] = [e.touches[0].clientX + proxy.offset[0], e.touches[0].clientY + proxy.offset[1]];
      if (proxy.socketStatus) {
        socket.emit('drawing', current)
      } else {
        drawing(current, proxy.offset);
        currentArray.push(JSON.parse(JSON.stringify(current)))
      }
      document.addEventListener('touchmove', handleTouchMove);
    }

    function handleMousemove(e) {
      if (current[0][0] == e.x && current[0][1] == e.y) { return }
      current[1] = [e.x + proxy.offset[0], e.y + proxy.offset[1]];
      if (proxy.socketStatus) { socket.emit('drawing', current) } else { drawing(current, proxy.offset); currentArray.push(JSON.parse(JSON.stringify(current))) }
      current[0] = [e.x + proxy.offset[0], e.y + proxy.offset[1]];
    }

    function handleTouchMove(e) {
      if (current[0][0] == e.touches[0].clientX && current[0][1] == e.touches[0].clientY) { return }
      current[1] = [e.touches[0].clientX + proxy.offset[0], e.touches[0].clientY + proxy.offset[1]];
      if (proxy.socketStatus) { socket.emit('drawing', current) } else {
        drawing(current, proxy.offset);
        currentArray.push(JSON.parse(JSON.stringify(current)));
      }
      current[0] = [e.touches[0].clientX + proxy.offset[0], e.touches[0].clientY + proxy.offset[1]];
    }

    function handleMouseUp(e) {
      document.removeEventListener('mousemove', handleMousemove);
      if (!proxy.socketStatus && e.target != 'div.toolBar') { localStorage.setItem('private', JSON.stringify(currentArray)) }
    }
    function handleTouchUp(e) {
      document.removeEventListener('touchmove', handleTouchMove);
      if (!proxy.socketStatus && e.target != 'div.toolBar') { localStorage.setItem('private', JSON.stringify(currentArray)) }
    }

    function cameraMove(offset) {
      coor = prompt('Enter coordinate', proxy.offset);
      while (coor != null) {
        c = coor.replace(/\s/g, '').replace(/，/, ',');
        if (c && arrayReg(`[${c}]`)) {
          proxy.offset = JSON.parse(`[${c}]`);
          coor = null;
        } else {
          coor = prompt('Coordinate format faild, enter coordinate', proxy.offset);
        }
      }
    }
    function addMask(ele = document.createElement('div')) {
      mask = document.createElement('div');
      mask.id = 'mask';
      document.body.appendChild(mask);
      mask.appendChild(ele);
      mask.addEventListener('click', (e) => { document.body.removeChild(mask) })
    }

    function getInfo() {
      infoWindow = document.createElement('div');
      infoWindow.style.backgroundColor = 'white';
      infoWindow.style.padding = '1rem';
      infoWindow.addEventListener('click', (e) => { e.stopPropagation(); });
      a = document.createElement('div');
      a.innerHTML = `Coordinate: ${proxy.offset}`;
      b = document.createElement('div');
      b.innerHTML = `LineWidth: ${current[2]}`;
      c = document.createElement('div');
      c.innerHTML = `Color: ${current[3]}`;
      d = document.createElement('div');
      d.innerHTML = `<a href='data:text/plain;charset=utf-8,${JSON.stringify(currentArray)}' download='export.data'>Export page data</a>`;
      infoWindow.appendChild(a);
      infoWindow.appendChild(b);
      infoWindow.appendChild(c);
      infoWindow.appendChild(d);
      addMask(infoWindow);
    }

    function drawingArr(data) {
      if (data) { currentArray = data };
      for (i = 0; i < currentArray.length; i++) {
        drawing(currentArray[i], proxy.offset)
      };
    }

    function drawing(d, offset = [0, 0]) {
      try {
        ctx.beginPath();
        ctx.lineCap = ctx.lineJoin = 'round';
        ctx.lineWidth = d[2];
        ctx.strokeStyle = d[3];
        a = [d[0][0] - offset[0], d[0][1] - offset[1]];
        b = [d[1][0] - offset[0], d[1][1] - offset[1]];
        ctx.moveTo(...a);
        ctx.lineTo(...b);
        ctx.stroke();
      } catch (err) { }
    }
  </script>
</head>

<body onload="init()">
  <div id="toolBox">
    <div class="toolBar" style="background-color: #fff;color: #008CBA;">
      <div class="switch">
        <input type="checkbox" id="socketSwitch" onchange="proxy.socketStatus = this.checked">
        <label for="socketSwitch">
          <div id="connStatus" style="height: 100%; width: 100%;"></div>
        </label>
      </div>
      <div style="height: 2rem;"></div>
      <div style="font-size:14px;font-size: 2rem; cursor: pointer;" onclick="cameraMove()" class="iconfont icondingwei">
      </div>
      <div style="height: 2rem;"></div>
      <div class="iconfont iconinfo" style="font-size: 14px;font-size: 2rem;cursor: pointer;" onclick="getInfo()"></div>
    </div>
  </div>
  <div id="coordinate"></div>
  <canvas id="paintCanvas">您的浏览器不支持HTML5 canvas标签。</canvas>
</body>

</html>